#include "compat_event_kqueue.h"
#include "compat_event_mach.h"

#if (TARGET_OS == OS_MACH || TARGET_OS == OS_UNIX)

#define ev_base_event 0

#define ev_poll_event EVFILT_READ
#define ev_conn_event EVFILT_WRITE

#define ev_read_event  EVFILT_READ
#define ev_write_event EVFILT_WRITE

#define ev_close_event EV_EOF
#define ev_clear_event EV_CLEAR


#define kevent_add_op     EV_ADD | EV_CLEAR
#define kevent_del_op     EV_DELETE | EV_CLEAR
#define kevent_op_enable  EV_ENABLE | EV_CLEAR
#define kevent_op_disable EV_DISABLE | EV_CLEAR

static int set_kevent_ctl(kevent_t* ptr, uint32_t ctl)
{
    rc_error(ptr != NULL, S_ERROR);
    ptr->ctl = ctl;
    return S_SUCCESS;
}

static uint32_t get_kevent_ctl(kevent_t* ptr)
{
    return ptr->ctl;
}
static int kqueue_op_ctl(kqueue_op_t* base, evoper_t* oper, kevent_t* ptr, const struct timespec* tm, int op)
{
    int rc = 0;

    uint32_t flags = get_kevent_ctl(ptr);

    if (op == ev_poll_event || op == ev_read_event) {
        struct kevent event;
        EV_SET(&event, oper->fd, EVFILT_READ, flags, 0, 0, oper);
        rc = base->sys_kevent(base->kqfd, &event, 1, NULL, 0, tm);
    }

    if (op == ev_conn_event || op == ev_write_event) {
        struct kevent event;
        EV_SET(&event, oper->fd, EVFILT_WRITE, flags, 0, 0, oper);
        rc = base->sys_kevent(base->kqfd, &event, 1, NULL, 0, tm);
    }

    if (rc == -1) {
        crterr err = crt_geterror();
        if (err == EINVAL) {
            loge("kqueue_op_ctl error %d, epfd=%d, fd=%d\n", errno, base->kqfd, oper->fd);
        } else if (err == ENOENT) {
            loge("kqueue_op_ctl error %d, epfd=%d, fd=%d\n", errno, base->kqfd, oper->fd);
        } else if (err != EPERM) {
            loge("kqueue_op_ctl error %d, epfd=%d, fd=%d\n", errno, base->kqfd, oper->fd);
        }
        return S_ERROR;
    }

    return S_SUCCESS;
}

#if (TARGET_OS == OS_MACH)

static int kqueue_notify_init(kqueue_op_t* base)
{
    base->notify_kev.ident = 0;
    base->notify_kev.filter = EVFILT_USER;
    base->notify_kev.data = 0;
    base->notify_kev.flags = EV_ADD | EV_CLEAR;
    base->notify_kev.fflags = 0;
    base->notify_kev.udata = 0;

    if (base->sys_kevent(base->kqfd, &base->notify_kev, 1, NULL, 0, NULL) == -1) {
        return S_ERROR;
    }
    return S_SUCCESS;
}
#endif

static int kqueue_hook_api(kqueue_op_t* base)
{
    base->sys_kqueue = (kqueue_fn)dlsym(RTLD_NEXT, "kqueue");
    rc_error(base->sys_kqueue != NULL, S_ERROR);

    base->sys_kevent = (kevent_fn)dlsym(RTLD_NEXT, "kevent");
    rc_error(base->sys_kevent != NULL, S_ERROR);

    return S_SUCCESS;
}
int kqueue_alloc(evloop_t* loop, int size)
{
    int rc = S_ERROR;

    kqueue_op_t* base = NULL;
    base = (kqueue_op_t*)loop->mm_malloc(loop, 1, sizeof(kqueue_op_t));
    rc_error(base != NULL, S_ERROR);

    base->size = size;

    rc = kqueue_hook_api(base);
    if (rc != S_SUCCESS)
        goto err;

    base->kqfd = base->sys_kqueue();
    if (base->kqfd == 0)
        goto err;

#if (TARGET_OS == OS_MACH)
    rc = kqueue_notify_init(base);
    if (rc != S_SUCCESS)
        goto err;
#endif

    base->poll = NULL;
    base->poll = loop->mm_malloc(loop, base->size, sizeof(struct kevent));
    if (base->poll == NULL)
        goto err;

    base->change = NULL;
    base->change = loop->mm_malloc(loop, base->size, sizeof(struct kevent));
    if (base->poll == NULL)
        goto err;
	
	
	evloop_queue_init(&base->queue);

    return evloop_bind_evbase(loop, base);

err:
    loop->mm_free(loop, base);
    return S_ERROR;
}
int kqueue_dealloc(evloop_t* loop)
{
    kqueue_op_t* base = (kqueue_op_t*)evloop_evbase(loop);
    loop->mm_free(loop, base->poll);
    loop->mm_free(loop, base->change);
    return S_SUCCESS;
}
int kqueue_handler(evloop_t* loop, ev_op ev)
{
    rc_error(loop != NULL, S_ERROR);
    kqueue_op_t* base = (kqueue_op_t*)evloop_evbase(loop);
    base->ev = ev;
    return S_SUCCESS;
}
int kqueue_alloc_evoper(evloop_t* loop, evoper_t* oper)
{
    rc_error(loop != NULL, S_ERROR);
    kqueue_op_t* base = (kqueue_op_t*)evloop_evbase(loop);
    rc_error(oper != NULL, S_ERROR);
	kevent_t* ptr = (kevent_t*)loop->mm_malloc(loop, 1, sizeof(kevent_t));
    rc_error(ptr != NULL, S_ERROR);

    evoper_bind_private(oper, ptr);

    evoper_op_init(oper);

    // Adding kqueue causes multiple event triggers
    // set_kevent_ctl(ptr, kevent_add_op);
    // kqueue_op_ctl(base, oper, ptr, NULL);

    return S_SUCCESS;
}
int kqueue_dealloc_evoper(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_ERROR);

    kqueue_op_t* base = (kqueue_op_t*)evloop_evbase(loop);
    rc_error(oper != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    set_kevent_ctl(ptr, kevent_del_op | kevent_op_disable);
    kqueue_op_ctl(base, oper, ptr, NULL, ev_read_event | ev_write_event);

    loop->mm_free(loop, evoper_private(oper));

    return S_SUCCESS;
}
int kqueue_enable_read(evloop_t* loop, evoper_t* oper, short event, ev_time_t tv)
{
    int rc = S_SUCCESS;
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    // add kqueue
    set_kevent_ctl(ptr, kevent_add_op | kevent_op_enable);
    kqueue_op_ctl(base, oper, ptr, NULL, ev_read_event);

    // recv
    //rc = mach_tcpsock_recv(oper, 0);

    return S_SUCCESS;
}
int kqueue_enable_write(evloop_t* loop, evoper_t* oper, short event, ev_time_t tv)
{
    int rc = S_SUCCESS;
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

	// send
	rc = evoper_tcpsock_send(oper, S_SUCCESS, 0, base->ev);
	if (rc == S_PENDING) {
		// add kqueue
		set_kevent_ctl(ptr, kevent_add_op | kevent_op_enable);
		kqueue_op_ctl(base, oper, ptr, NULL, ev_write_event);
	}
	
    return S_SUCCESS;
}
int kqueue_disable_read(evloop_t* loop, evoper_t* oper, short event)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    evoper_read_op(oper, op_ev_readed);

    set_kevent_ctl(ptr, kevent_del_op | kevent_op_disable);
    kqueue_op_ctl(base, oper, ptr, NULL, ev_read_event);

    return S_SUCCESS;
}
int kqueue_disable_write(evloop_t* loop, evoper_t* oper, short event)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    evoper_write_op(oper, op_ev_writed);

    set_kevent_ctl(ptr, kevent_del_op | kevent_op_disable);
    kqueue_op_ctl(base, oper, ptr, NULL, ev_write_event);

    return S_SUCCESS;
}
int kqueue_cancel(evloop_t* loop, evoper_t* oper)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);
    return S_SUCCESS;
}
int kqueue_open(evloop_t* loop, evoper_t* oper, const evaddr_t* addr)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);
    return S_SUCCESS;
}
int kqueue_poll(evloop_t* loop, evoper_t* oper)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    set_kevent_ctl(ptr, kevent_add_op | kevent_op_enable);

    return kqueue_op_ctl(base, oper, ptr, NULL, ev_poll_event);
}
int kqueue_add_connect(evloop_t* loop, evoper_t* oper, const evaddr_t* addr, ev_time_t tv)
{
    int rc = S_SUCCESS;

    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);

	kevent_t* ptr = (kevent_t*)evoper_private(oper);
    rc_error(ptr != NULL, S_ERROR);

    rc = evoper_sock_connect(oper, addr);

    if (rc == S_PENDING) {
        set_kevent_ctl(ptr, kevent_add_op | kevent_op_enable);
        kqueue_op_ctl(base, oper, ptr, NULL, ev_conn_event);
    }

    return rc;
}
int kqueue_del_connect(evloop_t* loop, evoper_t* oper)
{
    kqueue_op_t* base = (kqueue_op_t*)evoper_evbase(oper);
    rc_error(base != NULL, S_ERROR);
    return S_SUCCESS;
}

static int kqueue_cmd_poll(kqueue_op_t* base, evloop_t* loop, crt_msec timer)
{
    struct timespec timeout;
    timeout.tv_sec = timer / 1000;
    timeout.tv_nsec = (timer % 1000) * 1000000;

    int nfds = -1;

    nfds = base->sys_kevent(base->kqfd, NULL, 0, base->poll, base->size, &timeout);

    crterr err = crt_geterror();

    int i = 0;
    for (i = 0; i < nfds; i++) {
        evoper_t* oper = base->poll[i].udata;

        if (base->poll[i].flags & EV_ERROR) {
            complete_sock_close(loop, S_SUCCESS, err, oper, base->ev);
            continue;
        }

        if (base->poll[i].flags & EV_EOF) {
            evoper_eof_op(oper, ev_on);
        }

        if (base->poll[i].filter == EVFILT_READ) {
            if (oper->accept == op_ev_accepting) {
                complete_sock_accept(loop, S_SUCCESS, err, oper, NULL, base->ev);
                continue;
            }

            complete_sock_read(loop, S_SUCCESS, err, oper, base->ev);
            continue;
        }

        if (base->poll[i].filter == EVFILT_WRITE) {
            if (oper->conn == op_ev_connecting) {
                complete_sock_connect(loop, S_SUCCESS, err, oper, base->ev);
                continue;
            }
            complete_sock_write(loop, S_SUCCESS, err, oper, base->ev);

            if (evoper_writeing(oper) == S_SUCCESS)
				evoper_tcpsock_send(oper, S_SUCCESS, err, base->ev);

            continue;
        }
    }
    return S_SUCCESS;
}
static int kqueue_cmd_exit(const kqueue_op_t* base, evloop_t* loop)
{
    return S_SUCCESS;
}
static int kqueue_dispatch(evloop_t* loop, short event, crt_msec time)
{
    int rc = S_SUCCESS;

    kqueue_op_t* base = (kqueue_op_t*)evloop_evbase(loop);

    switch (event) {
    case op_cmd_poll:
        rc = kqueue_cmd_poll(base, loop, time);
        break;
    case op_cmd_exit:
        rc = kqueue_cmd_exit(base, loop);
        break;
    default:
        break;
    }

    return rc;
}
int kqueue_evop_t(evsysop_t* op)
{
    op->op_alloc = kqueue_alloc;
    op->op_dealloc = kqueue_dealloc;

    op->op_handler = kqueue_handler;

    op->op_alloc_evoper = kqueue_alloc_evoper;
    op->op_dealloc_evoper = kqueue_dealloc_evoper;

    op->op_enable_read = kqueue_enable_read;
    op->op_enable_write = kqueue_enable_write;
    op->op_disable_read = kqueue_disable_read;
    op->op_disable_write = kqueue_disable_write;

    op->op_cancel = kqueue_cancel;
    op->op_open = kqueue_open;
    op->op_poll = kqueue_poll;
    op->op_add_connect = kqueue_add_connect;
    op->op_del_connect = kqueue_del_connect;

    op->op_dispatch = kqueue_dispatch;

    return S_SUCCESS;
}
int keventt_set_oper(kevent_t* ptr, evoper_t* oper)
{
	ptr->oper = oper;
	return S_SUCCESS;
}
#endif
